/*****************************************************
*
*               COPYRIGHT (c) 2015-2017 Hargic Corporation
*                         All Rights Reserved
*
* The source code contained or described herein and all documents
* related to the source code ("Material") are owned by Hargic
* Corporation or its licensors.  Title to the Material remains
* with Hargic Corporation or its suppliers and licensors.
*
* The Material is protected by worldwide copyright and trade secret
* laws and treaty provisions. No part of the Material may be used,
* copied, reproduced, modified, published, uploaded, posted, transmitted,
* distributed, or disclosed in any way except in accordance with the
* applicable license agreement.
*
* No license under any patent, copyright, trade secret or other
* intellectual property right is granted to or conferred upon you by
* disclosure or delivery of the Materials, either expressly, by
* implication, inducement, estoppel, except in accordance with the
* applicable license agreement.
*
* Unless otherwise agreed by Hargic in writing, you may not remove or
* alter this notice or any other notice embedded in Materials by Hargic
* or Hargic's suppliers or licensors in any way.
*
** Author: shengda.huang
** Date: 2018-05-26
*
*****************************************************/

/***** Include files ********************************/
#include <stdio.h>
#include "am23xx_1wire.h"

/***** Defines **************************************/
#define AM23XX_SET_TEST_PIN_HIGH()   do {  \
                                        if (pfunc.test_pin_set != NULL) \
                                            pfunc.test_pin_set(AM23XX_GPIO_LEVEL_HIGH); \
                                     } while (0)

#define AM23XX_SET_TEST_PIN_LOW()   do {  \
                                        if (pfunc.test_pin_set != NULL) \
                                            pfunc.test_pin_set(AM23XX_GPIO_LEVEL_LOW); \
                                     } while (0)

/***** Define structure *****************************/

/***** Define constant ******************************/

/***** Define global ********************************/

static am23xx_func_t pfunc =
{
    .gpio_pullup        = NULL,
    .gpio_set_dir       = NULL,
    .gpio_set_output    = NULL,
    .gpio_get_level     = NULL,
    .delay_us           = NULL,
    .test_pin_set       = NULL,
};

/***** Define Prototype of functions ****************/

/**
 * @brief
 * @param  None
 * @retval None
 */
void am23xx_register_gpio_func(am23xx_func_t *func)
{
    pfunc.gpio_pullup       = func->gpio_pullup;
    pfunc.gpio_set_dir      = func->gpio_set_dir;
    pfunc.gpio_set_output   = func->gpio_set_output;
    pfunc.gpio_get_level    = func->gpio_get_level;
    pfunc.delay_us          = func->delay_us;
    pfunc.test_pin_set      = func->test_pin_set;
}

int am23xx_start(void)
{
    if (   pfunc.gpio_set_dir == NULL
        || pfunc.gpio_set_output == NULL
        || pfunc.delay_us == NULL
        || pfunc.gpio_pullup == NULL
        || pfunc.gpio_get_level == NULL)
        return -10;

    pfunc.gpio_set_dir(AM23XX_GPIO_DIR_OUTPUT);
    pfunc.gpio_set_output(AM23XX_GPIO_LEVEL_LOW);
    pfunc.delay_us(AM23XX_START_KEEP_LOW_TIME);
    pfunc.gpio_set_output(AM23XX_GPIO_LEVEL_HIGH);
    pfunc.delay_us(AM23XX_START_RELEASE_TIME);
    pfunc.gpio_set_output(AM23XX_GPIO_LEVEL_LOW);

    pfunc.gpio_pullup();
    pfunc.gpio_set_dir(AM23XX_GPIO_DIR_INPUT);

    // waiting for change to low
    int cnt = 0;
    while (pfunc.gpio_get_level() != AM23XX_GPIO_LEVEL_LOW)
    {
        if (cnt++ > AM23XX_REPONSE_TIMEOUT)
            return -3;
        pfunc.delay_us(1);
    }

    // client keep 80us low level
    // waiting for change to high
    cnt = 0;
    while (pfunc.gpio_get_level() != AM23XX_GPIO_LEVEL_HIGH)
    {
        if (cnt++ > AM23XX_REPONSE_TIMEOUT)
            return -1;
        pfunc.delay_us(1);
    }

    // client keep 80us high level
    // waiting for change to low
    cnt = 0;
    while (pfunc.gpio_get_level() != AM23XX_GPIO_LEVEL_LOW)
    {
        if (cnt++ > AM23XX_REPONSE_TIMEOUT)
            return -2;
        pfunc.delay_us(1);
    }

    AM23XX_SET_TEST_PIN_LOW();

    return 0;
}

/*
 *  @biref: reading bit should started in low level when enter this function
 */
static int am23xx_read_bit(void)
{
    if (   pfunc.delay_us == NULL
        || pfunc.gpio_get_level == NULL)
        return -10;

    AM23XX_SET_TEST_PIN_HIGH();

    // keep 50us low level
    int cnt = 0;
    while (pfunc.gpio_get_level() != AM23XX_GPIO_LEVEL_HIGH)
    {
        if (cnt++ > AM23XX_LOW_LEVEL_TIMEOUT)
        {
            AM23XX_SET_TEST_PIN_LOW();
            return -1;
        }
        pfunc.delay_us(1);
    }

    AM23XX_SET_TEST_PIN_LOW();

    /*
     *  @brief: reading keeping high level time
     */
    cnt = 0;
    while (pfunc.gpio_get_level() != AM23XX_GPIO_LEVEL_LOW)
    {
        if (cnt++ > AM23XX_HIGH_LEVEL_TIMEOUT)
            return -2;
        pfunc.delay_us(1);
    }

    if (cnt > AM23XX_BIT_LEVEL_THRESHOLD)
        return 1;

    return 0;
}

static int am23xx_read_byte(unsigned char *byte)
{
    int ret, i;

    for (i=0; i<8; i++)
    {
        ret = am23xx_read_bit();
        if (ret < 0)
        {
            printf("%s: read bit failed %d\n", __func__, ret);
            return ret;
        }

        (*byte) <<= 1;

        if (ret == 1)
            (*byte) |= 0x1;
    }

    return 0;
}

int am23xx_read_nbyte(unsigned char *byte, int btr)
{
    int ret, i;

    for (i=0; i<btr; i++)
    {
        ret = am23xx_read_byte(byte + i);
        if (ret < 0)
        {
            printf("%s: read byte failed %d\n", __func__, ret);
            return ret;
        }
    }
    return 0;
}

int am23xx_release(void)
{
    if (   pfunc.gpio_set_dir == NULL
        || pfunc.gpio_set_output == NULL
        || pfunc.delay_us == NULL
        || pfunc.gpio_pullup == NULL
        || pfunc.gpio_get_level == NULL)
        return -10;

    // client keep 50us low level to release bus
    int cnt = 0;
    while (pfunc.gpio_get_level() != AM23XX_GPIO_LEVEL_HIGH)
    {
        if (cnt++ > AM23XX_RELEASE_TIMEOUT)
            return -1;
        pfunc.delay_us(1);
    }

    AM23XX_SET_TEST_PIN_HIGH();

    return 0;
}

/* End of file */



